1. 싱글톤이란
싱글톤에 대한 설명
싱글톤(Singleton)은 디자인 패턴 중 하나로, 어떤 클래스가 최초 한 번만 메모리를 할당하고(static) 그 메모리에 인스턴스를 만들어 사용하는 디자인 패턴이다. 이렇게 하면, 메모리 낭비를 방지할 수 있다.
발전 과정
- 스프링은 기업용 온라인 서비스 기술을 지원하기 위해 탄생하였다.
- 스프링은 웹 어플리케이션이다.
- 웹 어플리케이션은 동시성 문제가 있다. ( 동시에 여러 고객이 요청함)
DI 컨테이너 동작 과정
- 고객이 요청할 때마다 객체를 새로 생성한다.
- 웹 어플리케이션은 고객이 계속 요청을 시도한다.
- 그렇다면 계속해서 고객이 요청한다면?
- 지금 Spring 이전에 Appconfig는 계속해서 유저가 접근할 때마다 객체를 생성한다
2. 싱글톤 패턴
싱글톤 패턴이란
클래스의 인스턴스가 딱 하나만 생성되는 것을 보장하는 디자인 패턴이다. 그래서 객체 인스턴스를 2개 이상 생성하지 못하도록 막는다.
- private 생성자를 사용해서 외부에서 임의로 생성하지 못하게 한다.
- 해당 객체의 인스턴스가 필요하면 getInstance() 메서드를 통해서만 조회할 수 있다.
- 딱 한개의 개발 인스턴스만 존재함으로, private으로 생성자를 사용해서 new 키워드로 객체 인스턴스가 생성되는 것을 막는다.
싱글톤 패턴에는 어떤 문제점이 있을까?
- 구현하는 코드가 많이 들어간다.
- 클라이언트가 구체 클래스에 의존한다(dip 위반)
- 테스트하기 어렵다
- 내부 속성에 대한 변경이 정말 어렵다.
3. 싱글톤 컨테이너
- 스프링 컨테이너느 싱글톤 패턴을 적용하지 않아도 객체 인스턴스를 싱글톤으로 관리한다.
- 스프링 컨테이너는 싱글톤 컨테이너 역할을 한다. 싱글톤 객체를 생성하고 관리하는 기능을 싱글톤 레지스트리라고 한다.
- 해당 테스트에서 getBean을 통해서 객체를 반환한다.
- 두개의 객체를 비교하였을 때 같은지에 대한 테스트를 진행한다.
- 스프링 컨테이너는 싱글톤이지만, 새로운 객체를 생성해서 반환하는 기능도 사용할 수는 있다.
4. 싱글톤 방식의 주의점
싱글톤 방식을 사용할 때 같은 인스턴스를 공유하기 때문에 값 접근에 있어 정말 조심해야 한다.
- 특정 클라이언트에 의존적인 필드가 있어서는 안된다.
- 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다.
- 필드 대신에 자바에서 공유되지 않는 지역변수, 파라미터 등을 사용해야 한다.
- 가급적 읽기만 가능하여야 한다.
예시
- price라는 필드를 공유하고, 주문과 주문한 금액을 조회하는 로직이 있다.
- 만약 주문 test1과 test2가 생성되었을 때
- test1의 주문 금액을 가져오면 어떻게될까?
같은 객체로 필드를 공유하였기 때문에 20000원이 된다.
해결 방법
- price 부분을 int로 넘겨주면 된다. (지역변수 사용)
5. 싱글톤 컨테이너 @Configuration
Appconfig를 의심해라
- 해당 코드는 다음과 같이 의존성 주입을 통해서 객체를 생성한다.
- memberService → new MemoryMemberRepository
- orderService → new MemoryMemberRepository
객체가 두번 생성되는데 싱글톤이 유지가 될 수 있을까?
확인해보자
- MemberServiceImpl.java 코드 변경
- OrderServiceImpl.java 코드 변경
- 싱글톤 테스트
- 해당 테스트는 성공이라고 뜬다.
- 분명 객체는 3번 생성할텐데 왜 다 같은 값이 나오는걸까?
AppConfig도 결국 Bean이다
앞서 스프링 컨테이너는 싱글톤 레지스트리라고 말했다. 하지만 아까처럼 3번 호출 하는 경우 다른 객체가 생성이 된다. 그래서 스프링은 클래스의 바이트코드를 조작하는 라이브러리가 있다.
한번 확인해보자
- 이상한 Enhance 어쩌구가 나온다.
- 스프링은 CGLIB라는 바이트코드 조작 라이브러릴 사용해서 AppConfig 클래스를 상속받은 다른 클래스를 만들고, 그 다른 클래스를 스프링 빈으로 등록한다.
- 해당 라이브러리는 스프링 Configureation에서 싱글톤을 보장해준다.
- 해당 라이브러리는 스프링 컨테이너를 조회해서 스프링 컨테이너에 등록된 Bean이면 반환하고, 없으면 새로 생성하고 스프링 컨테이너에 등록한다.
Spring에서 Bean을 Container에 등록할 때 @Configuration을 써야 하는 이유
- Configuration 어노테이션을 붙이지 않아도 Bean은 등록 된다.
- 하지만 CGLIB 기술이 적용되어 있지 않기 때문에 싱글톤 보장이 안된다.
- 따라서 DI 컨테이너를 작성할 때는 꼭 Configuration 어노테이션을 사용하길 바란다.